#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <setjmp.h>
////第一题、实现对一个整形数组的冒泡排序。
////（这里默认数组为[9,8,7,6,5,4,3,2,1,0]，让数组元素顺序按从小到大排序。）
////其实所谓冒泡排序啊，无非就是相邻的两个数进行比较，如果比较的结果符合我们的顺序要求，那就不变，否则进行位置互换即可。那这里的话无非几个步骤：
////1、准备工作先准备好；2、第①次比较：前两个数先比较，然后依次往后推，其实是要进行n-1(n为待比较的数)次比较之后才能让第①个数到后面的结果出现。然后，该数位置不动。
////3、可能涉及到一个循环或者递归，要仔细想一下。第②次比较：还是此时数组的前两个数进行比较，依次往后推，要进行n-1次比较之后才能让第②个数到达我们想要的位置。
////4、以此类推，那么直到n为1，意即只有一个数要进行比较，那该是一定是数组里面最后要动位置的数，但其实已经自动排好位置了，所以不用进行操作，此时可以退出循环。
//void bubble(int arr[],int left,int right,int sz)
//{
//    int j = 0;
//    int i = 0;
//    for (i = 1; 0 < sz - i; i++)//sz-i表示需要比较的次数；或者i = 0;0 < right - i;也行。
//    {
//        int left = 0;//每次循环都要重新定义left = 0,要不然left就是上次循环最后的值了。
//        while (left < right)
//        {
//            if (arr[left] > arr[left + 1])
//            {
//                j = arr[left];
//                arr[left] = arr[left + 1];
//                arr[left + 1] = j;
//            }
//            left++;
//        }
//    }
//}
//int main() {
//    int arr[10] = {9,8,7,6,5,4,3,2,1,0};
//    int left = 0;
//    int sz = sizeof(arr)/sizeof(arr[0]);
//    int right = sz -1;
//    bubble(arr,left,right,sz);
//    int i = 0;
//    for(i = 0;i <= right;i++)
//    {
//        printf("%d ",arr[i]);
//    }
//    return 0;
//}


//第二题、完成三子棋游戏代码。
//首先，很明显，上来就是菜单、循环；2.输入进去之后你记得那个棋盘的样子；3.它输入的中间其实是'\0'(空格) char '\0'(空格)；
jmp_buf env;//声明环境缓冲区
#define Row 3
#define Col 3
void menu()//不用接收参数
{
    printf("$$$$$$$$$-Please select-$$$$$$$$$\n");
    printf("$$$$$$$$$-   1. Play   -$$$$$$$$$\n");
    printf("$$$$$$$$$-   0. Exit   -$$$$$$$$$\n");
}

void chess(char arr[Row][Col],int row,int col)//不能定义成Row、Col,因为宏的名称要保持唯一一致性，不要重复定义它。
{
    int i = 0,j = 0;
    //初始化棋盘，问题来了，为什么要初始化棋盘？因为你已经默认全部都是0了，如果全部都是零，那还怎么输入进去？
    for(i = 0;i < row; i++)
    {
        for(j = 0;j < col;j++)
        {
            arr[i][j] = ' ';
        }
    }
}//初始化棋盘和显示棋盘不能放在同一个函数内，因为后文反复要用到“显示棋盘”函数。

void display(char arr[Row][Col],int row,int col)
{
    //初始化数组了，接下来形成棋盘了。
    //先打印一行： %c |,"|"最后一列不要的。换行， 然后___|,而后循环。
    int x = 0,y = 0;
    for(x = 0;x < row;x++)
    {
        for(y = 0;y < col;y++)
        {
            if(y < col -1)
                printf("  %c  |", arr[x][y]);
            else
                printf("  %c  ",arr[x][y]);
        }
        printf("\n");
        for(y = 0;y < col;y++)
        {
            if(x < row - 1 && y < col -1)
                printf("_____|");
            else if(x < row - 1 && y == col-1)
                printf("_____");
            else if(x == row -1 && y < col - 1)
                printf("     |");
            else
                printf("     ");
            //printf("\n");
        }
        printf("\n");//真的是因为自动换行导致“     |    |”  溢出。因为这里已经是第一行第一列全部搞定了，“___|”等不换行它就一直会在后面跟着的。
    }
    printf("\n");
}

void computer(char arr[Row][Col],int row,int col)//我们明白：1、电脑是要随机下棋的，行和列的范围都在0~2之间；
//2.下的棋要是不被占用的才能打印显示出来。
{
    printf("The computer coordinate is:\n");
    while(1)
    {
        int a = rand()%row;
        int b = rand()%col;
        if(arr[a][b] == ' ')
        {
            arr[a][b] ='#';
            break;
        }
    }
}

void victory(char arr[Row][Col],int row,int col)
{
    //判断胜负无非就是横、竖、对角线三个相同且不等于' '，连成一线则算该角色赢。
//    其实就是拿第一个元素跟后面的元素进行比较，如果有一个不相等，则没赢；如果都相等，那就赢。
    //不能同格子比较，写代码的时候很容易造成同格子比较。还不能两个相等就马上打印，是要遍历完之后确认都相等了才能打印。
    int a = 0,b = 0;
    int i = 0;
    //横着
    for(a = 0;a < row;a++) {
        for (b = 0; b < col; b++) {
            int count = 0;//再次进入循环时如果不重新清算，count会一直累加，超过预定的数，只有刚好等于row/col-1时才会达到我们预期的目标。
            for (i = 0; i < col; i++) {
                if (arr[a][b] == arr[a][i] && arr[a][b] != ' ' && b != i)
                    count++;//count计数，如果count等于2，表明进来了两次，也就是比较了两次。（设列为3）该格子跟其他格子都比较完了。
            }
            if (count == col - 1) {
                if (arr[a][b] == '*') {
                    printf("You win!\n");
                    longjmp(env, 4);//回到主函数
                } else {
                    printf("Computer win!\n");
                    longjmp(env, 4);//回到主函数
                }
            }
        }
    }
    //竖着
    //竖着的话其实就是横着的变量改为列的变量就行了。
    for(a = 0;a < row;a++) {
        for (b = 0; b < col; b++) {
            int count = 0;
            for (i = 0; i < row; i++) {
                if (arr[a][b] == arr[i][b] && arr[a][b] != ' ' && a != i)
                    count++;
            }
            if (count == row - 1) {
                if (arr[a][b] == '*') {
                    printf("You win!\n");
                    longjmp(env, 4);//回到主函数
                } else {
                    printf("Computer win!\n");
                    longjmp(env, 4);//回到主函数
                }
            }
        }
    }
    //对角线：还是老样子，同格子的不能比较。
    //从第一个元素开始的对角线
    int j = 0;
    for(a = 0;a < row;a++) {
        for (b = 0; b < col; b++) {
            int count = 0;
            for (i = 0,j = 0;i < row && j < col;i++,j++) {//双变量，除了表达式，其它条件都用逗号。
                if (arr[a][b] == arr[i][j] && arr[a][b] != ' ' && a != i && j != b )
                    count++;
            }
            //大if语句只能在b循环里面,你这不讲废话的吗？变量count只在b循环里面生效啊。
            if (count == row - 1) {
                if (arr[a][b] == '*') {
                    printf("You win!\n");
                    longjmp(env, 4);//回到主函数
                } else {
                    printf("Computer win!\n");
                    longjmp(env, 4);//回到主函数
                }
            }
        }
    }
    int x = 0;
    //第一行最后一个元素的对角线
    for(a = 0;a < row;a++) {
        for (b = col -1; b >= 0; b--) {
            int count_a = 0;
            for (i = a + 1,x = b - 1;i < row && x >= 0;i++,x--) {
                if (arr[a][b] == arr[i][x] && arr[a][b] != ' ')//记得把变量改成符合条件的。
                    count_a++;
            }
            if (count_a == row - 1) {
                if (arr[a][b] == '*') {
                    printf("You win!\n");
                    longjmp(env, 4);//回到主函数
                } else {
                    printf("Computer win!\n");
                    longjmp(env, 4);//回到主函数
                }
            }
        }
    }
}

void dogfall(char arr[Row][Col],int row,int col)
{
    //平局的话，意思就是说：上述四种情况都没有，且棋盘都下满了。
    int a = 0;
    int b = 0,count = 0;
    for(a = 0;a < row;a++)
    {
        for(b = 0;b < col;b++)
        {
            if(arr[a][b] != ' ')
            {
                count++;
            }
        }
    }
    if(count == row * col)
    {
        printf("Dogfall.\n");
        longjmp(env, 4);//回到主函数
    }
}

void Programmer(char arr[Row][Col],int row,int col)//程序员的坐标就与设计的一样了
{
    int a = 0,b = 0;
    printf("Please input coordinates:");
    scanf("%d %d",&a,&b);//输入之后让数组元素来接收，且是没有接受过数值的数组元素。
    //但是仍有顾虑：一个是输入的值在二维数组的范围内，另一个是输入之后该坐标没有被占用，那就可以显示。
    //自己写的时候少了一个while循环，仔细想想，循环是很有必要的，且只有符合条件才能break跳出循环。而且你记住这种循环的用法：while(1).
    while(1)
    {
        if(0 <= a && a <row && 0 <= b && b < col)
        {
            //没有被占用那就显示，被占用了就显示重新输入。
            if(arr[a][b] == ' ')
            {
                arr[a][b] = '*';//不用你printf,因为我后面要打印棋盘，自然会显现出来的。
                display(arr,Row,Col);//显示棋盘
                victory(arr,Row,Col);//判断胜负
                dogfall(arr,Row,Col);//如果能来到这里，表明要么平局，要么继续。
                if(arr[a][b] == '*')//如果输入正确，程序员下棋后电脑会马上下棋；否则电脑一直等待程序员下棋正确。
                {
                    computer(arr,Row,Col);//电脑下棋
                    display(arr,Row,Col);//显示棋盘
                    victory(arr,Row,Col);//判断胜负
                    dogfall(arr,Row,Col);//如果能来到这里，表明要么平局，要么继续。
                }
                break;
            }
            else
            {
                printf("This coordinate was inputted.Please input again.\n");
                break;
            }
        }else
        {
            printf("Error,please input again.\n");
            break;
        }
    }
    //display(arr,Row,Col);//显示棋盘
}

void player(char arr[Row][Col],int row,int col)
{
    printf("eg:the first coordinate is (1 1).\n");
    printf("Please input coordinates:");
    int a = 0,b = 0;
    scanf("%d %d",&a,&b);//输入之后让数组元素来接收，且是没有接受过数值的数组元素。
    while(1)
    {
        if(1 <= a && a <= row && 1 <= col && b <= col)
        {
            if(arr[a-1][b-1] ==' ')
            {
                arr[a-1][b-1] = '*';
                display(arr,Row,Col);//显示棋盘
                victory(arr,Row,Col);//判断胜负
                dogfall(arr,Row,Col);//如果能来到这里，表明要么平局，要么继续。
                if(arr[a-1][b-1] == '*')//如果输入正确，玩家下棋后电脑会马上下棋；否则电脑一直等待玩家下棋正确。
                {
                    computer(arr,Row,Col);//电脑下棋
                    display(arr,Row,Col);//显示棋盘
                    victory(arr,Row,Col);//判断胜负
                    dogfall(arr,Row,Col);//如果能来到这里，表明要么平局，要么继续。
                }
                break;
            }else
            {
                printf("This coordinate was inputted.Please input again.\n");
                break;
            }
        }else
        {
            printf("Error,please input again.\n");
            break;
        }
    }
}

void game() {
    //要先定义一个二维数组，然后再在二维数组里面填充内容。
    char arr[Row][Col] = {0};
    chess(arr, Row, Col);//初始化棋盘
    display(arr, Row, Col);//显示棋盘
    int identity = 0;//玩家开始下棋，规定玩家下棋为'*',电脑下棋用‘#’。
    do {
        printf("<----- 2.Programmer Or 3.Common Player ----->\n");
        printf("Please choose your role:");
        scanf("%d", &identity);
        while (identity) {
            if (identity == 2) {
                Programmer(arr, Row, Col);//程序员下棋，下完棋之后就要来判断胜负了。无论谁输谁赢，都要直接退出比赛。
                continue;
                //continue的作用是：如果它执行了，那么它后面的语句时不会再执行的，直接来到while循环中的if的判断表达式部分，判断其是否为真，真就进入下一次循环。
                //所以当Progammer()函数全部都执行之后，游戏自然也就结束了，game()函数进入了contiune;，其后面的就不会再执行了。
            } else if (identity == 3) {
                player(arr, Row, Col);//玩家下棋
                //现在有个问题，玩家输入错误之后，电脑不会等他。————问题解决了，嵌套在函数里面。
                continue;
            } else {
                printf("Error,please select again.\n");
                break;//跳出里面的while循环，来到外面的do循环。
            }
        }
    } while (identity);
}


int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));//初始化随机数生成器
    if(setjmp(env) != 4)//函数跳转
    {
        do
        {
            menu();
            printf("The number is:");
            scanf("%d",&input);
            switch(input)
            {
                case 1:
                    game();
                    break;
                case 0:
                    printf("Quit!\n");
                    break;
                default:
                    printf("Error,please input again.\n");
                    break;
            }
        }
        while(input);
    }else//(setjmp(env) == 4)//如果放前面的话就会再走一遍do while的循环。
    {
        do
        {
            menu();
            printf("The number is:");
            scanf("%d",&input);
            switch(input)
            {
                case 1:
                    game();
                    break;
                case 0:
                    printf("Quit!\n");
                    break;
                default:
                    printf("Error,please input again.\n");
                    break;
            }
        }
        while(input);
    }
    return 0;
}